// Copyright [2022] <Tianyu Zhu, Shizhen Zhao, Zihang Ren>

#include <algorithm> // std::random_shuffle
#include <assert.h>
#include <cstdlib> // std::rand, std::srand
#include <fstream>
#include <iostream>

#include "graph.h"

namespace
{
    void ReadLinksFromFile(const std::string &file_path,
                           std::vector<Link> *links)
    {
        std::string line;
        std::ifstream in(file_path + "topo.csv");
        if (in.fail())
        {
            std::cout << "File not found" << std::endl;
            return;
        }
        int line_cnt = 0;
        while (getline(in, line) && in.good())
        {
            ++line_cnt;
            if (line_cnt == 1)
                continue;
            // 把line里的单元格数字字符提取出来，“,”为单元格分隔符
            std::vector<std::string> buffer;
            std::string str;
            for (int i = 0; i < line.size(); ++i)
            {
                if (line[i] == ',')
                {
                    buffer.push_back(str);
                    str.clear();
                    continue;
                }
                str.push_back(line[i]);
            }
            assert(buffer.size() >= 8);
            links->emplace_back(
                atoi(buffer[0].c_str()), atoi(buffer[1].c_str()),
                atoi(buffer[2].c_str()), atof(buffer[4].c_str()),
                atoi(buffer[5].c_str()), atof(buffer[6].c_str()),
                atoi(buffer[7].c_str()));
            if (atoi(buffer[7].c_str()) > 0)
            {
                std::string tmp;
                for (int i = 0; i < str.size(); ++i)
                {
                    if (str[i] == '|')
                    {
                        links->back().srlgs.push_back(atoi(tmp.c_str()));
                        tmp.clear();
                        continue;
                    }
                    tmp.push_back(str[i]);
                }
                links->back().srlgs.push_back(atoi(tmp.c_str()));
            }
        }
        in.close();
    }

    bool Compare(Link *a, Link *b)
    {
        return a->weight < b->weight;
    }

} // namespace

void Path::Print() const
{
    if (path_info.empty())
    {
        std::cout << "Empty Path\n";
        return;
    }
    std::cout << "Path: " << path_info[0];
    for (int i = 1; i < path_info.size(); ++i)
    {
        std::cout << "->" << path_info[i];
    }
    std::cout << ".\nCost is: " << cost << ", Delay is: "
              << delay << ".\n";
}

void Graph::swap_link_loc(Link *a, Link *b)
{
    Link buf = *a;
    a->bandwidth = b->bandwidth;
    a->cost = b->cost;
    a->delay = b->delay;
    a->link_id = b->link_id;
    a->source_id = b->source_id;
    a->srlg_num = b->srlg_num;
    a->ter_id = b->ter_id;
    a->used = b->used;
    a->weight = b->weight;
    a->srlgs = b->srlgs;
    a->cousin = b->cousin;
    b->bandwidth = buf.bandwidth;
    b->cost = buf.cost;
    b->cousin = buf.cousin;
    b->delay = buf.delay;
    b->link_id = buf.link_id;
    b->source_id = buf.source_id;
    b->srlg_num = buf.srlg_num;
    b->srlgs = buf.srlgs;
    b->ter_id = buf.ter_id;
    b->used = buf.used;
    b->weight = buf.weight;
}

bool Graph::check_is_cousin_link_pair(const Link *a, const Link *b)
{
    if (a->source_id == b->source_id && a->ter_id == b->ter_id && a->cost == b->cost && a->delay == b->delay && a->srlg_num == b->srlg_num)
    {
        for (int i = 0; i < a->srlgs.size(); ++i)
        {
            if (a->srlgs[i] != b->srlgs[i])
                return false;
        }
        return true;
    }
    return false;
}

void Graph::find_cousin_link(Link *link, std::vector<Link *> link_group)
{
    for (Link *link_in_group : link_group)
    {
        if (check_is_cousin_link_pair(link, link_in_group))
        {
            link->cousin.push_back(link_in_group);
            link_in_group->cousin.push_back(link);
        }
    }
}

Graph::Graph(const std::string &file_path)
{
    srlg_group_.reserve(10000);
    for (int i = 0; i < 10000; ++i)
    {
        srlg_group_.push_back({});
    }
    ReadLinksFromFile(file_path, &links_);
    for (Link link : links_)
    {
        bp_links_.push_back(link);
    }
    // std::srand(0);
    // std::random_shuffle(links_.begin(), links_.end());
    int maxi_node_id = -1;
    for (Link &link : links_)
    {
        if (link.source_id > maxi_node_id)
            maxi_node_id = link.source_id;
        if (link.ter_id > maxi_node_id)
            maxi_node_id = link.ter_id;
        if (link.srlg_num != 0)
        {
            for (int i = 0; i < link.srlgs.size(); ++i)
            {
                srlg_group_[link.srlgs[i]].push_back(&link);
            }
        }
        nodes_.insert(link.source_id);
        nodes_.insert(link.ter_id);
    }
    size_ = maxi_node_id + 1;
    node_to_egress_links_.clear();
    node_to_ingress_links_.clear();
    bp_node_to_egress_links_.clear();
    node_to_egress_links_.reserve(size_);
    node_to_ingress_links_.reserve(size_);
    bp_node_to_egress_links_.reserve(size_);
    int size = node_to_ingress_links_.size();
    for (NodeId i = 0; i < size_; ++i)
    {
        // assert(nodes_.find(i) != nodes_.end());
        node_to_egress_links_.push_back({});
        bp_node_to_egress_links_.push_back({});
        node_to_ingress_links_.push_back({});
    }
    for (Link &link : links_)
    {
        find_cousin_link(&link, node_to_egress_links_[link.source_id]);
        node_to_egress_links_[link.source_id].push_back(&link);
        node_to_ingress_links_[link.ter_id].push_back(&link);
    }
    for (Link &link : bp_links_)
    {
        bp_node_to_egress_links_[link.source_id].push_back(&link);
    }
}

void Graph::SortLinks()
{
    for (NodeId i = 0; i < size_; ++i)
    {
        std::sort(node_to_egress_links_[i].begin(),
                  node_to_egress_links_[i].end(), Compare);
        std::sort(node_to_ingress_links_[i].begin(),
                  node_to_ingress_links_[i].end(), Compare);
    }
}

void Flow::Print() const
{
    std::cout << "\n"
              << "--------"
              << "\n";
    std::cout << "Flow Id: " << id << ", Source node: " << from
              << ", Destination node: " << to << ", Delay upperbound: "
              << delay_ub << ", Delay lowerbound: " << delay_lb
              << ", Bandwidth requirement: " << bandwidth << ",\n"
              << "Is Delaydiff: " << is_diff << ", Diff Range: "
              << diff << ", Separation type: " << type << ", Theory opt cost: "
              << opt_cost << "\n";
}

Demand::Demand(const std::string &file_path)
{
    std::string line;
    std::ifstream in(file_path + "tunnel.csv");
    if (in.fail())
    {
        std::cout << "File not found" << std::endl;
        return;
    }
    int case_cnt = 0;
    while (getline(in, line) && in.good())
    {
        ++case_cnt;
        if (case_cnt == 1)
            continue;
        // 把line里的单元格数字字符提取出来，“,”为单元格分隔符
        std::vector<std::string> buffer;
        std::string str;
        for (int i = 0; i < line.size(); ++i)
        {
            if (line[i] == ',')
            {
                buffer.push_back(str);
                str.clear();
                continue;
            }
            str.push_back(line[i]);
        }
        buffer.push_back(str);
        assert(buffer.size() >= 6);
        flows_.emplace_back(
            atoi(buffer[0].c_str()), atoi(buffer[1].c_str()),
            atoi(buffer[2].c_str()), atof(buffer[3].c_str()),
            atof(buffer[4].c_str()), atoi(buffer[5].c_str()),
            atoi(buffer[6].c_str()), atof(buffer[7].c_str()),
            atoi(buffer[8].c_str()), atof(buffer[9].c_str()));
    }

    in.close();
}
